package com.itheima.health.security;

import com.alibaba.fastjson.JSON;
import com.itheima.health.entity.Result;
import com.itheima.health.pojo.Permission;
import com.itheima.health.pojo.Role;
import com.itheima.health.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.*;

/**
 * @author tian
 * @TIME 2023/1/6 9:01 上午
 * @description
 */
@Slf4j
@WebFilter(urlPatterns = "/*")
public class HealthSecurityFilter implements Filter {
    //定义不需要登录就可以访问的URI
    private String[] unLoginAccessUrlList = new String[]{
            "/user/login",//后台管理系统登录
            "/mobile/validateCode/send",//移动端发送验证码
            "/mobile/login/smsLogin",//移动端登录
    };
    //定义登录后需要授权访问的URI，没在此map的，登录即可访问
    private Map<String, String> loginAuthUrlMap = new HashMap<>();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //读取资源需要的权限
        initAuthUrlMap();
    }

    //初始化需要权限的路径（只包含本项目已开发功能,比如【套餐编辑】【套餐删除】功能未开发所以未包含）
    private void initAuthUrlMap() {
        //未在列表中的，默认用户登录就可以访问
        //在列表中的，需要判断当前用户是否有匹配的权限，才可以访问
        //把权限和路径关系存进loginAuthUrlMap
        //套餐相关
        loginAuthUrlMap.put("/checkgroup/findAll", "SETMEAL_ADD");//套餐新增或套餐编辑都应有此权限 实际开发中设计套餐新增权限和套餐编辑权限应该是同时拥有的，此处我们不再细分
        loginAuthUrlMap.put("/checkgroup/upload", "SETMEAL_ADD");//套餐新增
        loginAuthUrlMap.put("/setmeal/add", "SETMEAL_ADD");//套餐新增
        loginAuthUrlMap.put("/setmeal/findPage", "SETMEAL_QUERY");//查询套餐
        //loginAuthUrlMap.put("","SETMEAL_EDIT");//套餐编辑 该项目没有开发本功能，所以我们也没有请求路径
        //loginAuthUrlMap.put("", "SETMEAL_DELETE");//套餐删除 该项目没有开发本功能，所以我们也没有请求路径

        //检查组相关
        loginAuthUrlMap.put("/checkitem/findAll", "CHECKGROUP_ADD");//新增检查组或编辑检查组都应有此权限 实际开发中设计新增检查组权限和编辑检查组权限应该是同时拥有的，此处我们不再细分
        loginAuthUrlMap.put("/checkgroup/add", "CHECKGROUP_ADD");//新增检查组
        loginAuthUrlMap.put("/checkgroup/findPage", "CHECKGROUP_QUERY");//查询检查组
        loginAuthUrlMap.put("/checkgroup/edit", "CHECKGROUP_EDIT");//编辑检查组
        loginAuthUrlMap.put("/checkgroup/findCheckItemIdsByCheckGroupId", "CHECKGROUP_EDIT");//编辑检查组
        loginAuthUrlMap.put("/checkgroup/findById", "CHECKGROUP_EDIT");//编辑检查组
        loginAuthUrlMap.put("/checkgroup/deleteCheckGroupitemById", "CHECKGROUP_DELETE");//删除检查组

        //检查项相关
        loginAuthUrlMap.put("/checkitem/add", "CHECKITEM_ADD");//新增检查项
        loginAuthUrlMap.put("/checkitem/findPage", "CHECKITEM_QUERY");//查询检查项
        loginAuthUrlMap.put("/checkitem/findById", "CHECKITEM_EDIT");//编辑检查项
        loginAuthUrlMap.put("/checkitem/edit", "CHECKITEM_EDIT");//编辑检查项
        loginAuthUrlMap.put("/checkitem/delete", "CHECKITEM_DELETE");//删除检查项

        //其他（一些没加进来的权限是因为本项目仍未开发相关功能）
        loginAuthUrlMap.put("/ordersetting/**", "ORDERSETTING");//预约设置
        loginAuthUrlMap.put("/report/**", "REPORT_VIEW");//统计分析
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        //拦截所有请求
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        //获取uri
        String uri = request.getRequestURI();
        log.info("[权限检查过滤器],uri:{}", uri);

        //判断uri是否可以不用登录，直接访问
        boolean isNoLoginAccess = checkURI(uri);
        if (isNoLoginAccess) {
            //不需要登录，直接放行
            log.info("[权限检查过滤器],允许放行");
            filterChain.doFilter(request, response);
            return;
        }

        //如果需要登录，判断是否登录过
        //根据uri带不带mobile来判断是移动端还是后台管理端的用户请求
        AntPathMatcher pathMatcher = new AntPathMatcher();
        if (pathMatcher.match("/mobile/**", uri)) {
            if (request.getSession().getAttribute("member") != null) {
                log.info("[权限检查过滤器],移动端用户登录，允许放行");
                filterChain.doFilter(request, response);
                return;
            } else {
                //如果未登录，返回未登录的错误信息，前端处理错误信息显示
                log.info("[权限检查过滤器],未登录，无权访问uri:{}", uri);
                response.setContentType("application/json; charset=UTF-8");
                response.getWriter().write(JSON.toJSONString(new Result(false, "请重新登录")));
            }
        } else {
            if (request.getSession().getAttribute("user") != null) {
                User user = (User) request.getSession().getAttribute("user");
                log.info("[权限检查过滤器],已登录，检查当前用户是否有访问权限，用户信息:{}", user.getUsername());
                //检查当前用户是否有此uri的访问权限
                boolean isLoginAuth = checkLoginAuthURI(uri, user);
                if (isLoginAuth) {
                    filterChain.doFilter(request, response);
                    return;
                } else {
                    // 已登录过，但是访问的资源没有权限，前端处理错误信息显示
                    response.setContentType("application/json; charset=UTF-8");
                    response.getWriter().write(JSON.toJSONString(new Result(false, "无操作权限")));
                    return;
                }
            }
            //如果未登录，返回未登录的错误信息，前端处理错误信息显示
            log.info("[权限检查过滤器],未登录，无权访问uri:{}", uri);
            response.setContentType("application/json; charset=UTF-8");
            response.getWriter().write(JSON.toJSONString(new Result(false, "请重新登录")));
        }
    }

    //检查当前用户是否有此uri的访问权限
    private boolean checkLoginAuthURI(String reqUri, User user) {
        AntPathMatcher pathMatcher = new AntPathMatcher();
        //先用uri匹配loginAuthUrlMap的key，再获取需要的权限
        //如果没有匹配到key，说明该路径不需要权限，可以直接访问
        Set<String> keySet = loginAuthUrlMap.keySet();
        String currKeyUrl = null;
        for (String urlKey : keySet) {
            if (pathMatcher.match(urlKey, reqUri)) {
                currKeyUrl = urlKey;
            }
        }
        if (currKeyUrl == null) {
            //没有匹配到key说明该路径可以直接访问
            log.info("[权限检查过滤器],当前用户访问的URI，无需权限，直接访问，reqUri:{}", reqUri);
            return true;
        }
        //此时说明该路径需要权限才可以访问
        //拿到该路径对应的权限访问keyword
        String authKeyword = loginAuthUrlMap.get(currKeyUrl);
        log.info("[权限检查过滤器],当前用户访问的uri需要授权，currKeyUrl:{},keyWord:{}", currKeyUrl, authKeyword);
        log.info("[权限检查过滤器],当前用户信息：user:{}", user);
        //获取用户的权限列表，判断该用户是否有此uri的权限
        Set<Role> roles = user.getRoles();
        for (Role role : roles) {
            Set<Permission> permissions = role.getPermissions();
            for (Permission p : permissions) {
                if (p.getKeyword().equals(authKeyword)) {
                    log.info("[权限检查过滤器],当前用户匹配权限，允许通过");
                    return true;
                }
            }
        }
        log.info("[权限检查过滤器],当前用户匹配权限不匹配，未通过");
        return false;
    }

    //判断是否不需要登录，可以访问的资源
    private boolean checkURI(String reqURI) {
        //AntPathMatcher工具类 可以匹配通配符 第一个参数是路径匹配规则 第二个参数是请求路径 写反可能会有BUG
        AntPathMatcher pathMatcher = new AntPathMatcher();
        //遍历unLoginAccessUrlList数组，让每条路径都与当前的URI比对
        for (String url : unLoginAccessUrlList) {
            if (pathMatcher.match(url, reqURI)) {
                log.info("[权限检查过滤器],匹配放行的url:{}", url);
                return true;
            }
        }
        return false;
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

}
